package math;

import java.util.Arrays;

public class Fraction implements Cloneable, Comparable, java.io.Serializable {
	protected final long numerator_;
	protected final long denominator_;

	/** Returns the numerator **/
	public final long numerator() { return numerator_; }

	/** Returns the denominator **/
	public final long denominator() { return denominator_; }

	/** Creates a Fraction equal in value to num / den **/
	public Fraction(long num, long den) {
		// normalize while constructing
		boolean numNonnegative = (num >= 0);
		boolean denNonnegative = (den >= 0);
		long a = numNonnegative? num : -num;
		long b = denNonnegative? den : -den;
		long g = gcd(a, b);
		numerator_ = (numNonnegative == denNonnegative)? (a / g) : (-a / g);
		denominator_ = b / g;
	}

	/** Create a fraction with the same value as Fraction f **/
	public Fraction(Fraction f) {
		numerator_ = f.numerator();
		denominator_ = f.denominator();
	}
	public Fraction(long numr){
		numerator_ = numr;
		denominator_ = 1;
	}

	public String toString() { 
		if (denominator() == 1) 
			return "" + numerator();
		else
			return numerator() + "/" + denominator(); 
	}

	public Object clone() { return new Fraction(this); }

	/** Return the value of the Fraction as a double **/
	public double asDouble() { 
		return ((double)(numerator())) / ((double)(denominator()));
	}

	/** 
	 * Compute the nonnegative greatest common divisor of a and b.
	 * (This is needed for normalizing Fractions, but can be
	 * useful on its own.)
	 **/
	public static long gcd(long a, long b) { 
		long x;
		long y;

		if (a < 0) a = -a;
		if (b < 0) b = -b;

		if (a >= b) { x = a; y = b; }
		else        { x = b; y = a; }

		while (y != 0) {
			long t = x % y;
			x = y;
			y = t;
		}
		return x;
	}

	/** return a Fraction representing the negated value of this Fraction **/
	public Fraction negative() {
		long an = numerator();
		long ad = denominator();
		return new Fraction(-an, ad);
	}

	/** return a Fraction representing 1 / this Fraction **/
	public Fraction inverse() {
		long an = numerator();
		long ad = denominator();
		return new Fraction(ad, an);
	}


	/** return a Fraction representing this Fraction plus b **/
	public Fraction plus(Fraction b) {
		long an = numerator();
		long ad = denominator();
		long bn = b.numerator();
		long bd = b.denominator();
		return new Fraction(an*bd+bn*ad, ad*bd);
	}

	/** return a Fraction representing this Fraction plus n **/
	public Fraction plus(long n) {
		long an = numerator();
		long ad = denominator();
		long bn = n;
		long bd = 1;
		return new Fraction(an*bd+bn*ad, ad*bd);
	}

	/** return a Fraction representing this Fraction minus b **/
	public Fraction minus(Fraction b) {
		long an = numerator();
		long ad = denominator();
		long bn = b.numerator();
		long bd = b.denominator();
		return new Fraction(an*bd-bn*ad, ad*bd);
	}

	/** return a Fraction representing this Fraction minus n **/
	public Fraction minus(long n) {
		long an = numerator();
		long ad = denominator();
		long bn = n;
		long bd = 1;
		return new Fraction(an*bd-bn*ad, ad*bd);
	}


	/** return a Fraction representing this Fraction times b **/
	public Fraction times(Fraction b) {
		long an = numerator();
		long ad = denominator();
		long bn = b.numerator();
		long bd = b.denominator();
		return new Fraction(an*bn, ad*bd);
	}

	/** return a Fraction representing this Fraction times n **/
	public Fraction times(long n) {
		long an = numerator();
		long ad = denominator();
		long bn = n;
		long bd = 1;
		return new Fraction(an*bn, ad*bd);
	}

	/** return a Fraction representing this Fraction divided by b **/
	public Fraction dividedBy(Fraction b) {
		long an = numerator();
		long ad = denominator();
		long bn = b.numerator();
		long bd = b.denominator();
		return new Fraction(an*bd, ad*bn);
	}

	/** return a Fraction representing this Fraction divided by n **/
	public Fraction dividedBy(long n) {
		long an = numerator();
		long ad = denominator();
		long bn = n;
		long bd = 1;
		return new Fraction(an*bd, ad*bn);
	}

	/** return a number less, equal, or greater than zero
	 * reflecting whether this Fraction is less, equal or greater than 
	 * the value of Fraction other.
	 **/
	public int compareTo(Object other) {
		Fraction b = (Fraction)(other);
		long an = numerator();
		long ad = denominator();
		long bn = b.numerator();
		long bd = b.denominator();
		long l = an*bd;
		long r = bn*ad;
		return (l < r)? -1 : ((l == r)? 0: 1);
	}

	/** return a number less, equal, or greater than zero
	 * reflecting whether this Fraction is less, equal or greater than n.
	 **/

	public int compareTo(long n) {
		long an = numerator();
		long ad = denominator();
		long bn = n;
		long bd = 1;
		long l = an*bd;
		long r = bn*ad;
		return (l < r)? -1 : ((l == r)? 0: 1);
	}

	public boolean equals(Object other) {
		return compareTo((Fraction)other) == 0;
	}

	public boolean equals(long n) {
		return compareTo(n) == 0;
	}
	/** Returns the index of the first positive element in the array **/
	public static int indxfirstpostv(Fraction []fr){
		for (int i=0;i<fr.length;i++)
			if(fr[i].compareTo(0)==1)
				return i;
		return -1;

	}

	/** Returns the index of the minimum element in the array **/
	public static int indxmin(Fraction []fr){
		for(int i=0;i<fr.length;i++)
			if(fr[i].equals(min(fr)))
				return i;
		return -1;

	}
	/** Returns the minimum of the array **/
	public static Fraction min(Fraction [] fr){
		double[] frd= new double [fr.length];
		for (int i=0;i<fr.length;i++) frd[i]= fr[i].asDouble();
		Arrays.sort(frd);
		for(int j=0;j<fr.length;j++)
			if(fr[j].asDouble()==frd[0])
				return fr[j];
		return null;

	}
	/** Returns the maximum of the array **/
	public static Fraction max(Fraction [] fr){
		double[] frd= new double [fr.length];
		for (int i=0;i<fr.length;i++) frd[i]= fr[i].asDouble();
		Arrays.sort(frd);
		for(int j=0;j<fr.length;j++)
			if(fr[j].asDouble()==frd[frd.length-1])
				return fr[j];
		return null;
	}

	public int hashCode() {
		return (int) (numerator_ ^ denominator_);
	}

}
